/*  led-drv.c - The simplest kernel module.

* Copyright (C) 2013 - 2016 Xilinx, Inc
*
*   This program is free software; you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation; either version 2 of the License, or
*   (at your option) any later version.

*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License along
*   with this program. If not, see <http://www.gnu.org/licenses/>.

*/

#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/mm.h>
#include <linux/exportfs.h>
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/of_dma.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/dma/xilinx_dma.h>
#include <linux/of_reserved_mem.h>
#define WIDTH 1920
#define HEIGHT 1204
#define PIC_CHANNAL 3 
#define MAX_SIZE (WIDTH*HEIGHT*PIC_CHANNAL)

//字符设备结构体
struct vdma_dev
{
	dev_t 	devid;			//设备号
	int major;				//主设备号
	int minor;				//副设备号
	struct cdev	cdev;		//字符设备
	struct class *class;
	struct device *device;
	struct dma_chan  *rx_chan;		//dma通道
	struct dma_device *rx_dev; 
	struct scatterlist  rx_sg;
    struct dma_async_tx_descriptor *rxd;		//传输描述符  
    enum dma_ctrl_flags flags; 				//传输标志位
	struct xilinx_vdma_config config;
	struct dma_interleaved_template xt;
	dma_cookie_t rx_cookie;
	wait_queue_head_t read_queue;
	int irq_reprot;
	int read_mem;  //指向要读取的帧缓冲区
};

#define 	DEVICE_COUNT		1
#define 	DEVICE_NAME		"vdma0"
dma_addr_t dma_dst;			//源地址和目的地址 
unsigned char *dst;
phys_addr_t dst_phy;
u32 temp1; 

struct vdma_dev vdma;
struct dma_tx_state dma_status;
int my_count = 200;

static int vdma_probe(struct platform_device *dev);
static int vdma_remove(struct platform_device *dev);
void rx_callback(void *dma_async_param);
static int vdmarx_task(struct vdma_dev *vdma);


static int vdma_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &vdma;

	vdma.irq_reprot = 0;

	memset(dst , 0 , MAX_SIZE);
    // vdmarx_task(&vdma);

	printk("VDMA0 open!\r\n");
	return 0;
}


static int vdma_release(struct inode *inode, struct file *filp)
{
	struct vdma_dev *vdma =  filp->private_data;
    dmaengine_terminate_all(vdma->rx_chan);
	filp->private_data = NULL;
	printk("VDMA0 release!\r\n");
	return 0;

}

static ssize_t vdma_write(struct file *file_p, const char __user *buf, size_t len, loff_t *loff_t_p)
{
	int ret;
	char writebuf;
	printk("at vdma_write\r\n");
	vdmarx_task(&vdma);

	return 0;
}

static unsigned int vdma_poll(struct file *filp, struct poll_table_struct *wait)
{
	int mask = 0;

	poll_wait(filp, &(vdma.read_queue), wait);
	printk("vdma.irq_reprot:%d\r\n", vdma.irq_reprot);
	if (vdma.irq_reprot == 1)
	{
		vdma.irq_reprot = 0;
		mask |= (POLLIN | POLLRDNORM);
	}
	printk("return at vdma_poll\r\n");
	return mask;
}




ssize_t vdma_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	struct vdma_dev *vdma =  filp->private_data;
	int ret = 0;
	ret = copy_to_user(buf,dst,MAX_SIZE);
	if(ret<0)
	{
		printk("read fail!\r\n");
	}	
	return 0;
}


static const struct file_operations vdma_fops = {
	.owner		= THIS_MODULE,
	.open		= vdma_open,
	.read		= vdma_read,
	.write      = vdma_write,
	.poll 		= vdma_poll,
	.release	= vdma_release,
};


static int vdma_init(struct vdma_dev *dev)
{
	int ret = 0;
	//1.创建字符设备号
	dev->major = 0;
	if(dev->major)		//给定设备号
	{
		dev->devid = MKDEV(dev->major,0);
		ret = register_chrdev_region(dev->devid,DEVICE_COUNT,DEVICE_NAME);
	}
	else
	{
		ret = alloc_chrdev_region(&dev->devid,0,DEVICE_COUNT,DEVICE_NAME);
	}
	if(ret < 0)
	{
		printk("fail to register!\r\n");
		goto fail_register;
	}
	dev->major = MAJOR(dev->devid);
	dev->minor = MINOR(dev->devid);
	printk("VDMA0 MAJOR = %d!\r\n",dev->major);
	printk("VDMA0 MINOR = %d!\r\n",dev->minor);
	//2.添加字符设备
	dev->cdev.owner = THIS_MODULE;
	cdev_init(&dev->cdev,&vdma_fops);
	ret = cdev_add(&dev->cdev,dev->devid,DEVICE_COUNT);
	if(ret<0)
	{
		printk("fail to create cdevice!\r\n");
		goto fail_cdev;
	}
	//3.自动创建设备节点
	dev->class = class_create(THIS_MODULE, DEVICE_NAME);			//创建类
	if(IS_ERR(dev->class))
	{
		printk("fail to create class!\r\n");
		goto fail_class;
	}
	dev->device = device_create(dev->class,NULL,dev->devid,NULL,DEVICE_NAME);	//创建设备
	if(IS_ERR(dev->device))
	{
		printk("fail to create device!\r\n");
		goto fail_device;
	}
/*
	longmen_vdma_0: longmen_vdma_0@0 {
		compatible ="longmen vdma4";
		dmas = <&axi_vdma_0 1>;
		dma-names = "vdma0_rx";
		xlnx,num-fstores = <0x3>;	//VDMA设备节点中配置的帧缓冲区数
		memory-region = <&vdma_0_reserved>;
	}; 
*/
	printk("VDMA0 init!\r\n");	
	return 0;

fail_device:
	class_destroy(dev->class);
fail_class:
	cdev_del(&dev->cdev);
fail_cdev:
	unregister_chrdev_region(dev->devid,DEVICE_COUNT);
fail_register:
	printk("VDMA0 init fail!\r\n");
	return -1;
}

static void vdma_exit(struct vdma_dev *dev)
{
	device_destroy(dev->class,dev->devid);					//摧毁设备
	class_destroy(dev->class);									//摧毁类
	cdev_del(&dev->cdev);										//注销字符设备
	unregister_chrdev_region(dev->devid,DEVICE_COUNT);			//注销字符设备号
	printk("VDMA0 exit!\r\n");	
}

static const struct of_device_id vdma_of_match[]={
	{.compatible = "longmen vdma4"},
	{/* Sentinel*/},
};

static struct platform_driver vdma_driver = {
	.driver = {
		.name = "vdma0",
		.of_match_table = vdma_of_match,
	},
	.probe = vdma_probe,
	.remove = vdma_remove,
};

static int vdma_probe(struct platform_device *dev)	
{
	int ret = 0;
	printk("height:%dn, width:%dn\r\n",HEIGHT,WIDTH);
	ret = vdma_init(&vdma);
	if(ret < 0)
	{
		printk("vdma_init fail!\r\n");
		goto fail_init;
	}
    dev->dev.coherent_dma_mask = 0xffffffff;	
	//dma_mask与coherent_dma_mask这两个参数表示它能寻址的物理地址的范围,其中dma_coherent_mask则作用于申请一致性DMA缓冲区
	//1.申请通道
	vdma.rx_chan = dma_request_chan(&dev->dev, "vdma0_rx");		//与设备树的dma通道名字相同
	if (vdma.rx_chan == NULL) 
	{
		printk("request_rx_chan fail!\r\n");
		goto fail_rx_chan;
	}
	// vdma.flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;   
	vdma.rx_dev = vdma.rx_chan->device;

	ret = of_reserved_mem_device_init(&dev->dev);
	if(ret < 0)
	{
		printk("vdma0 mem_init fail!\r\n");
		printk("ret mem_init = %d\r\n", ret);
		goto fail_mem_init;
	}
	//2.申请源地址和目标地址
	//static inline dma_addr_t dma_map_single(struct device *dev, void *ptr,size_t size, enum dma_data_direction dir)
	//static inline void *dma_alloc_coherent(struct device *dev, size_t size,dma_addr_t *dma_handle, gfp_t gfp) 
	//dma_alloc_coherent需要soc集成一个cache-coherent-interconnect硬件
	dst = dma_alloc_coherent(&dev->dev,MAX_SIZE, &dma_dst, GFP_KERNEL);//size:  想要申请的内存长度，单位为bytes
	if ( dst== NULL)
    {
        printk("can't alloc buffer for dst\r\n");
         ret = -ENOMEM;
		goto fail_dst;
    }

	
	printk("VDMA0 dma_dst = %llx\r\n",dma_dst);
	printk("VDMA0 dst = %x\r\n",(int)dst);


	memset(&vdma.config, 0, sizeof(struct xilinx_vdma_config));

	/* Set up hardware configuration information */
	vdma.config.park = 1;
	xilinx_vdma_channel_set_config(vdma.rx_chan, &vdma.config);
	init_waitqueue_head(&vdma.read_queue);
	// vdmarx_task(&vdma); 
	printk("VDMA0 probe!\r\n");
	return 0;
fail_dst:
	of_reserved_mem_device_release(&dev->dev);
fail_mem_init:
	dmaengine_terminate_all(vdma.rx_chan);
	dma_release_channel(vdma.rx_chan);
fail_rx_chan:
	vdma_exit(&vdma);
fail_init:	
	return -1;
}



static int vdmarx_task(struct vdma_dev *vdma)
{
	vdma->flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
	//3.获取传输描述符
	vdma->xt.dst_start = dma_dst;		//第一个块的源总线地址
	vdma->xt.dir = DMA_DEV_TO_MEM;
	vdma->xt.numf = HEIGHT;				//static unsigned int vsize = 32;  要从源读取的字节数
	vdma->xt.sgl[0].size = WIDTH*PIC_CHANNAL;		//static unsigned int hsize = 64;
	vdma->xt.sgl[0].icg = 0;	//此块的最后一个src/dst地址之后和下一块的第一个src/dst地址之前要跳转的字节数
	vdma->xt.frame_size = 1;	//帧中的块数
	vdma->xt.src_sgl     = 0;
	vdma->xt.src_inc     = 0;
	vdma->xt.dst_inc     = 0;
	vdma->xt.dst_sgl     = 0;
	vdma->rxd = vdma->rx_dev->device_prep_interleaved_dma(vdma->rx_chan,&vdma->xt,vdma->flags);
	if(vdma->rxd == NULL)
	{
		printk("fail to rxd\r\n");
		return -1;
	}

	

	// sg_init_table(&vdma->rx_sg, 1);
	// sg_dma_address(&vdma->rx_sg) = dma_dst;
	// sg_dma_len(&vdma->rx_sg) = MAX_SIZE;
	// vdma->rxd = vdma->rx_dev->device_prep_slave_sg(vdma->rx_chan, &vdma->rx_sg, 1, DMA_DEV_TO_MEM, vdma->flags, NULL);
	// // vdma->rxd = dmaengine_prep_slave_single(vdma->rx_chan, dma_dst, MAX_SIZE, DMA_DEV_TO_MEM, vdma->flags);
	// if (vdma->rxd == NULL)
	// {
	// 	printk("fail to rxd\r\n");
	// 	return -1;
	// }

	vdma->rxd->callback = rx_callback;	//rx通道回调函数，rx通道的device_prep_dma_memcpy任务此函数执行
	vdma->rxd->callback_param = NULL;
	vdma->rx_cookie = vdma->rxd->tx_submit(vdma->rxd);	//提交描述符，执行tx通道的device_prep_dma_memcpy任务

	if (dma_submit_error(vdma->rx_cookie))
	{  
        printk("DMA rx submit failed\r\n");  
    } 	
	dma_async_issue_pending(vdma->rx_chan); 
	// ljg change 8.30 22:08 
	// while(dma_async_is_tx_complete(vdma->rx_chan, vdma->rx_cookie, NULL, NULL) != DMA_COMPLETE){
	// 	printk("transfet no finish\r\n");
	// }hui ka zai zhe ge xun huan li

	// while (1)
	// {
	// 	--my_count;
	// 	if(my_count == 0){
	// 		break;
	// 	}
	// 	enum dma_status  status = dmaengine_tx_status(vdma->rx_chan,
    //                                 vdma->rx_cookie,
    //                                 &dma_status);
	// 	printk("status:%d", status);// DMA_IN_PROGRESS
	// 	printk("residue:%d", dma_status.residue);// 0
	// }
	

	// printk("DMA transfer start successfully.\r\n");
	// printk("VDMA2 rx dst :%d\r\n",*dst);
	return 0;
}

void rx_callback(void *dma_async_param)  
{

	vdma.irq_reprot = 1;
	wake_up_interruptible(&vdma.read_queue);
	printk("DMA0 transfer complete.\r\n");
    // vdmarx_task(&vdma);
	// printk("VDMA2 rx finish!\r\n");
	// printk("VDMA2 rx dst+10 :%d\r\n",*(dst+10));
} 

static int vdma_remove(struct platform_device *dev)
{

	
	dma_free_coherent(&dev->dev, MAX_SIZE, dst, dma_dst);
	//static inline void dma_free_coherent(struct device *dev, size_t size,void *cpu_addr, dma_addr_t dma_handle)
	of_reserved_mem_device_release(&dev->dev);
	dma_release_channel(vdma.rx_chan);
	vdma_exit(&vdma);	
	printk("VDMA0 remove!\r\n");
	return 0;
}

//驱动入口函数
static int __init dma_init(void)
{
	//注册platform驱动
	platform_driver_register(&vdma_driver);
	return 0;
}

//驱动出口函数
static void __exit dma_exit(void)
{
	platform_driver_unregister(&vdma_driver);
}

module_init(dma_init);
module_exit(dma_exit);

/* 驱动描述信息 */  
MODULE_AUTHOR("LONGMEN");   
MODULE_LICENSE("GPL"); 